/**
 * 生产者与消费者
 * 
 * @author fireway
 * @since 2019年 02月 23日 星期六 06:34:53 CST
 */
package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class WaitPerson implements Runnable {
    private Restaurant mRestaurant;

    public WaitPerson(Restaurant r) {
        mRestaurant = r;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    while (mRestaurant.mMeal == null) {
                        wait();
                    }
                }
                System.out.println("Waitperson got " + mRestaurant.mMeal);
                synchronized (mRestaurant.mChef) {
                    mRestaurant.mMeal = null;
                    mRestaurant.mChef.notifyAll();
                }
            }
        } catch (InterruptedException e) {
            System.out.println("WaitPerson interrupted");
            e.printStackTrace();
        }
    }
}

class Chef implements Runnable {
    private Restaurant mRestaurant;

    private int mCount = 0;

    public Chef(Restaurant r) {
        mRestaurant = r;
    }

    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    while (mRestaurant.mMeal != null) {
                        wait();
                    }
                }

                if (++mCount == 10) {
                    System.out.println("Out of food, closing");  // 食材用光了，停止了
                    mRestaurant.sExecutorService.shutdownNow();
                }
                System.out.println("Order up!");  // 上菜啦
                synchronized (mRestaurant.mWaitPerson) {
                    mRestaurant.mMeal = new Meal(mCount);
                    mRestaurant.mWaitPerson.notifyAll();
                }

                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            System.out.println("Chef interrupted");
            e.printStackTrace();
        }
    }
}

class Meal {
    private final int mOrderNum;

    public Meal(int orderNum) {
        mOrderNum = orderNum;
    }

    @Override
    public String toString() {
        return "Meal " + mOrderNum;
    }
}

public class Restaurant {
    /* package */ ExecutorService sExecutorService = Executors.newCachedThreadPool();

    /* package */ Meal mMeal;

    /* package */ WaitPerson mWaitPerson = new WaitPerson(this);

    /* package */ Chef mChef = new Chef(this);

    public Restaurant() {
        sExecutorService.execute(mChef);
        sExecutorService.execute(mWaitPerson);
    }

    public static void main(String[] args) {
        new Restaurant();
    }
}
